
#include <string.h>
#include "hj212packet.h"
#include "list.h"
#include "enprodev.h"
#include "hj212message.h"
#include "util.h"

typedef struct _tag_hj_packet_priv_dat_t
{
    char* datetime;
    int st;
    int cn;
    char* pwd;
    char* mn;
    struct list_head* current;
} HJPPMsg;


// static unsigned short _hj212p_crc16(char *data, unsigned int length)
// {
//     if (NULL == data || 0 == length) return 0;

//     unsigned short j, check;
//     unsigned short crc_reg = 0xFFFF;
//     unsigned int i;

//     for (i = 0; i < length; i++)
//     {
//         crc_reg = (crc_reg >> 8) ^ data[i];
//         for (j = 0; j < 8; j++)
//         {
//             check = crc_reg & 0x0001;
//             crc_reg >>= 1;
//             if (check == 0x0001)
//             {
//                 crc_reg ^= 0xA001;
//             }
//         }
//     }

//     return crc_reg;
// }



typedef struct _tag_hj212_data_
{
    struct list_head list;
    char* head;
    int hlen;
    char* str;
    int slen;
} Hj212PDta;




static int _hj212p_add_str(char* buffer, char* str)
{
    int length = strlen(str);
    if ( buffer && str )
    {
        strncpy(buffer, str, length);
    }
    return length;
}
// QN=20160801090000001;ST=32;CN=2061;PW=123456;MN=010000A8900016F000169DC0;Flag=5;
#define HJ212P_DATA_DATETIME_LENGTH             23
#define HJ212P_HEADER_LENGTH                    99
//                                                //; pnm  //cp=&&&&   datetime
#define HJ212P_ENVPRO_DATA_LENGTH_MAX           (950 - 8 - 17 - 7 - HJ212P_DATA_DATETIME_LENGTH) 
#define HJ212P_DATA_LENGTH_MAX                  1024
#define HJ212P_LENGTH_MAX                       1036     
#define HJ212P_SIZE_MAX                         9999



static char* _hj212p_init_header(char* qn, HJPPMsg* msg, char flag, int pum, int pon)
{
    char* str = NULL;
    if ( msg && qn )
    {
        str = (char*)malloc(HJ212P_HEADER_LENGTH+1);
        sprintf(str, "QN=%s;ST=%d;CN=%d;PW=%s;MN=%s;Flag=%d;PUNM=%d;PON=%d;", qn, msg->st, msg->cn, msg->pwd, msg->mn, flag, pum, pon);
    }
    
    return str;
}



typedef struct _hj212p_temp_dat_packet_t
{
    struct list_head list;
    struct list_head head;
    int pnum;
    char* qn;
} TPDNode;



static void _hj212p_free_dta_list(struct list_head* list)
{
    Hj212PDta* pd = NULL;
    Hj212PDta* pdtemp = NULL;
    TPDNode* tpd = NULL;
    TPDNode* tpdtemp = NULL;

    if ( list )
    {
        list_for_each_entry(tpd, list, list)
        {
            free(tpdtemp);
            list_for_each_entry(pd, &tpd->head, list)
            {
                free(pdtemp);
                free(pd->head);
                free(pd->str);
                pdtemp = pd;
            }
            free(pdtemp);

            free(tpd->qn);
            tpdtemp = tpd;
        }

        free(tpdtemp);
    }
}


static int _hj212p_add_pnum_header(struct list_head* list, HJPPMsg* msg, char flag)
{
    int ret = (msg && list);
    Hj212PDta* pd = NULL;
    TPDNode* tpd = NULL;
    int pno = 0, pnum = 0;
  
    if ( ret )
    {
        list_for_each_entry(tpd, list, list)
        {
            pnum = tpd->pnum;
            pno = 0;
            //printf("hj212pdat: %p\n", list_entry(tpd->head.next, Hj212PDta, list));
            list_for_each_entry(pd, &tpd->head, list)
            {
                ++pno;
                pd->head = _hj212p_init_header(tpd->qn, msg, flag, pnum, pno);
                if (pd->head )
                    pd->hlen = strlen(pd->head);
                else
                {
                    printf("no enough space fo pack header\n");
                    ret = 0;
                    break;
                }
            }
        }
    }

    return ret;
}

void _hj212p_print_dta_list(struct list_head* list)
{
    //char buffer[HJ212P_LENGTH_MAX] = { 0 };
    Hj212PDta* pd = NULL;
    TPDNode* tpd = NULL;

    list_for_each_entry(tpd, list, list)
    {
        list_for_each_entry(pd, &tpd->head, list)
        {
            //strncpy(buffer, pd->head, pd->hlen);
            //strncpy(buffer+pd->hlen, pd->str, pd->slen);
            //buffer[pd->hlen+pd->slen+1] = 0;
            printf("print start ..\n\r");
            printf("hlen: %d  slen: %d\n", pd->hlen, pd->slen);
            printf("head: %s\n", pd->head);
            printf("str: %s\n", pd->str);
            printf("print end ..\n\r");
        }
        
    }
}


static Hj212PDta* _hj212p_creat_data(char* buff, int length)
{
    Hj212PDta* pdt = (Hj212PDta*) malloc(sizeof(Hj212PDta));
    char* pstr = (char*)malloc(length);
    
    if ( pdt && pstr )
    {
        strncpy(pstr, buff, length-1);
        pstr[length-1] = 0;
        pdt->str = pstr;
        pdt->slen = length-1;
    }
    else
    {
        free(pstr);
        free(pdt);
    }

    return pdt;
}

static void _hj212p_add_tpdnode(struct list_head* head, TPDNode* tpdnode, int pnum, const char* qn)
{
    char* m_qn = (char*)malloc(strlen(qn) + 1);

    if ( head && tpdnode && m_qn )
    {
        tpdnode->pnum = pnum;
        strcpy(m_qn, qn);
        tpdnode->qn = m_qn;
        list_add_tail(&tpdnode->list, head);
    }
}


static int _hj212p_add_pdata_to_list(char* buffer, int length, struct list_head* head)
{
    int ret = (buffer && head);
    
    if ( ret )
    { 
        Hj212PDta* pdt = _hj212p_creat_data(buffer, length);
        if ( pdt )
        {
            list_add_tail(&pdt->list, head);
        }
        else
        {
            printf("no enough memory to malloc hj212pdt ...\n");
            ret = 0;
        }
    }

    return ret;
}

static void _hj212p_from_pdat_create_socket_packet(HJ212PackFact* fact, struct list_head* list)
{
    TPDNode* tpd = NULL;
    Hj212PDta* pd = NULL;
    char packet[HJ212P_LENGTH_MAX+1] = { 0 };
    char buffer[HJ212P_DATA_LENGTH_MAX+1] = { 0 };
    if ( fact && fact->prdata && list )
    {
        HJPPMsg* ppmsg = (HJPPMsg*)fact->prdata;

        list_for_each_entry(tpd, list, list)
        {
            list_for_each_entry(pd, &tpd->head, list)
            {
                memset(buffer, 0, HJ212P_DATA_LENGTH_MAX+1);
                memset(packet, 0, HJ212P_LENGTH_MAX+1);
                int len = pd->hlen + pd->slen + strlen(ppmsg->datetime) + strlen("CP=&&DataTime=;&&"); //CP=&&DataTime=xxxxx;&& 
                snprintf(buffer, len+1, "%s%s%s;%s%s", pd->head, "CP=&&DataTime=", ppmsg->datetime, pd->str, "&&");
                //printf("buffer: %s\n", buffer);
                unsigned short crc16 = hj212_crc16(buffer, len);
                
                snprintf(packet, len+13, "##%04u%s%02X%02X\r\n", len, buffer, (crc16 >> 8), (crc16 & 0x00ff));
                //buffer[len+13] = 0;
                

                HJ212PakNode* node = (HJ212PakNode*)malloc(sizeof(HJ212PakNode));
                if ( node )
                {
                    node->str = (char*)malloc(len+12);
                    if ( node->str )
                    {
                        strncpy(node->str, packet, len+12);
                        node->len = len+12;
                        list_add_tail(&node->list, &fact->head);
                        //printf("total: (%d)   socket: %s\n", len, packet);
                    }

                }

            }
        }
    }
}


void _hj212p_from_envpro_ctreat_packet(HJ212PackFact* fact, EnvProDev* epd)
{
    EnvProNode *epnode = NULL;
    struct list_head head;
    TPDNode* tpdnode = (TPDNode*)malloc(sizeof(TPDNode));
    char buffer[HJ212P_LENGTH_MAX+1] = { 0 };
    int bufleng = 0, pnum = 0;
    
    if ( epd && fact && tpdnode )
    {
        HJPPMsg* msg = (HJPPMsg*)fact->prdata;
        INIT_LIST_HEAD(&head);
        INIT_LIST_HEAD(&tpdnode->head);
        strcpy(msg->datetime, local_t_h212t(epd->avg_stime(epd)));
        char* qn = Hj212_qn_new();

        epd->begin(epd);
        while ( !epd->is_end(epd)  )
        {
            epnode = epd->current(epd); 
            if ( (bufleng + epnode->len) < HJ212P_ENVPRO_DATA_LENGTH_MAX )
            {
                bufleng += _hj212p_add_str((buffer+bufleng), epnode->avg);
            }
            else
            {
                ++pnum;
                if ( HJ212P_SIZE_MAX == pnum )
                {
                    _hj212p_add_tpdnode(&head, tpdnode, pnum, qn);
                    tpdnode = (TPDNode*)malloc(sizeof(TPDNode));
                    if ( !tpdnode )
                    {
                        printf("no enough memory to malloc tpdnode ...\n");
                        break;
                    }
                    free(qn);
                    qn = Hj212_qn_new();
                    pnum = 0;
                }
                else if ( !_hj212p_add_pdata_to_list(buffer, bufleng, &tpdnode->head) )
                    break;

                bufleng = 0;
            }
            epd->next(epd);
        }
        
        if ( bufleng && tpdnode )
        {
            ++pnum;
            if ( _hj212p_add_pdata_to_list(buffer, bufleng, &tpdnode->head) )
                _hj212p_add_tpdnode(&head, tpdnode,pnum, qn);
            else
                printf("no enough memory to malloc hj212pdt at (%d) ...\n", __LINE__);
        }
        _hj212p_add_pnum_header(&head, fact->prdata, 7);
        //_hj212p_print_dta_list(&head);
        free(qn);
        _hj212p_from_pdat_create_socket_packet(fact, &head);
        _hj212p_free_dta_list(&head);
    }

}

/*
 char* datetime;
    int st;
    int cn;
    char* pwd;
    char* mn;
*/
void _hj212p_init_ppmsg(HJPFPData* msg, char* pw, char* mn)
{
    HJPPMsg* ppmsg = ((msg) ? (HJPPMsg*)msg : NULL);

    if ( ppmsg )
    {
        ppmsg->st = 22;
        ppmsg->cn = 2061;
        ppmsg->pwd = (pw) ? strdup(pw) : NULL;
        ppmsg->mn = (mn) ? strdup(mn) : NULL;
        ppmsg->datetime = (char*) malloc(16);
        ppmsg->current = NULL;
    }

}


static void _hj212p_destroy_list(HJ212PackFact* fact)
{
    HJ212PakNode* node = NULL;
    HJ212PakNode* temp = NULL;

    int count = 0;
    list_for_each_entry(node, &fact->head, list)
    {   
        ++count;
        free(temp);
        free(node->str);
        temp = node;
    }
    free(temp);

    INIT_LIST_HEAD(&fact->head);

    printf("free HJ212PakNode count (%d) \n", count);
}

static void _hj212p_begin(struct _tag_hj212_packet_t* fact)
{
    if ( fact && fact->prdata )
    {
        HJPPMsg* msg = (HJPPMsg*) fact->prdata;
        msg->current = fact->head.next;
    }

}

static int _hj212p_is_end(struct _tag_hj212_packet_t* fact)
{
    int ret = 0;

    if ( fact && fact->prdata )
    {
        HJPPMsg* msg = (HJPPMsg*)fact->prdata;

        ret = (msg->current == &fact->head) ? 1 : 0;
    }

    return ret;
}


static void _hj212p_next(struct _tag_hj212_packet_t* fact)
{
    if ( fact && fact->prdata )
    {
        HJPPMsg* msg = (HJPPMsg*)fact->prdata;

        msg->current = msg->current->next;
    }
}


static HJ212PakNode* _hj212p_current(struct _tag_hj212_packet_t* fact)
{
    HJ212PakNode* node = NULL;

    if ( fact && fact->prdata )
    {
        HJPPMsg* msg = (HJPPMsg*)fact->prdata;
        node = list_entry(msg->current, HJ212PakNode, list);
    }

    return node;
}

static void _hj212_print(struct _tag_hj212_packet_t* fact)
{
    HJ212PakNode* node = NULL;
    int count = 0;
    list_for_each_entry(node, &fact->head, list)
    {   
        ++count;
        printf("(%s)\n\r", node->str);
    
    }
    printf("hj212 node count: (%d) socket packet print end...\n", count);
}

HJ212PackFact* hj212_pack_fact_new(void)
{
    HJ212PackFact* fact = malloc(sizeof(HJ212PackFact));
    HJPPMsg* ppmsg = (HJPPMsg*) malloc(sizeof(HJPPMsg));
    
    if (fact && ppmsg )
    {
        fact->prdata = ppmsg;
        INIT_LIST_HEAD(&fact->head);

        fact->from_envpro_socket = _hj212p_from_envpro_ctreat_packet;
        fact->init_ppmsg = _hj212p_init_ppmsg;
        fact->destroy_list = _hj212p_destroy_list;
        fact->begin = _hj212p_begin;
        fact->is_end = _hj212p_is_end;
        fact->next = _hj212p_next;
        fact->current = _hj212p_current;
        fact->print = _hj212_print;
    }
    else
    {
        if ( ppmsg ) free(ppmsg);
        if ( fact ) free(fact);
        fact = NULL;
    }

    return fact;

}


HJ212PackFact* hj212_pack_fact_destroy(HJ212PackFact* fact)
{
    if ( fact )
    {
        if ( fact->prdata )
        {
            HJPPMsg* ppmsg = (HJPPMsg*)fact->prdata;
            free(ppmsg->datetime);
            free(ppmsg->pwd);
            free(ppmsg->mn);
            free(ppmsg);
        }
        fact->destroy_list(fact);
        free(fact);
    }

    return NULL;
}